library(SingleCellExperiment)
library(tidyverse)
library(igraph)
library(scran)

devtools::load_all("~/miloR/")

## Setup to use python in Rmd
library(reticulate)
reticulate::use_condaenv("emma_env")

theme_dimred <- function( ... ){
  theme(axis.ticks =element_blank(), axis.text = element_blank(), plot.title = element_text(hjust=0.5), ... )
  }
import scanpy as sc
import pandas as pd
import numpy as np
## To show plots inline
import matplotlib.pyplot as plt
import sys,os
plt.switch_backend('agg')
sys.path.insert(1, '/nfs/team205/ed6/bin/thATAC/preprocess_utils/')
import atac_utils 
import scipy.sparse
from pathlib import Path

# sc._settings.ScanpyConfig.figdir = Path(r.outdir)

Load data

Load anndata object, downloaded from here following the link from Park et al. 2020

rna_adata = sc.read_h5ad("/nfs/team205/ed6/data/Park_scRNAseq/HTA08.v01.A06.Science_human_tcells.raw.h5ad")
rna_adata.X = rna_adata.raw.X
rna_adata.X = scipy.sparse.csc_matrix(rna_adata.X)

Load MOFA projection, to use as reduced dimensionality object

mofa_dims <- read.csv("/nfs/team205/ed6/data/thymus_data/thymus_MOFA_projection.csv") %>%
  column_to_rownames("cell")

## Filter just the scRNA-seq cells and factors 4 knn graoh construciton
mofa_dims <- as.matrix(mofa_dims[rownames(py$rna_adata$obs),1:5])

Convert anndata to SingleCellExperiment object

adata <- py$rna_adata
cnt <- t(adata$X)
rownames(cnt) <- adata$var_names$to_list()
colnames(cnt) <- adata$obs_names$to_list()
logCnt <- log2(cnt + 1)
pca <- prcomp(t(vx))

# Create the SingleCellExperiment object
sce <- SingleCellExperiment(assay=list(counts=cnt, logcounts=logCnt), colData = adata$obs)

reducedDim(sce) <- mofa_dims
reducedDimNames(sce) <- "MOFA"
sce

## Save SingleCellExperiment
saveRDS(sce, "/nfs/team205/ed6/data/Park_scRNAseq/HTA08.v01.A06.Science_human_tcells.SingleCellExperiment.RDS")
sce <- readRDS("~/Downloads/HTA08.v01.A06.Science_human_tcells.SingleCellExperiment.RDS")
sce
class: SingleCellExperiment 
dim: 33694 50514 
metadata(0):
assays(2): counts logcounts
rownames(33694): TSPAN6 TNMD ... RP11-107E5.4 RP11-299P2.2
rowData names(0):
colnames(50514): FCAImmP7179369-AAACCTGAGCCCAATT FCAImmP7179369-AAACCTGAGCCTATGT ...
  Human_colon_16S7985397-TTTGCGCAGAGCCCAA Human_colon_16S7985397-TTTGGTTAGTACGCGA
colData names(14): Sample donor ... cell types umap_density_Age
reducedDimNames(1): MOFA
altExpNames(0):
object.size(sce)
16486280 bytes

Cells in different samples where sorted using different FACS gates, which affect significantly the cell type composition of different samples. To simplify interpretation of DA analysis, I retain only samples that where obtained from total tissue and CD45+ cells, which have a similar cell type composition.

keep_cells <- which(sce$sort %in% c("TOT", "45P"))
sce <- sce[,keep_cells]
sce
class: SingleCellExperiment 
dim: 33694 38081 
metadata(0):
assays(2): counts logcounts
rownames(33694): TSPAN6 TNMD ... RP11-107E5.4 RP11-299P2.2
rowData names(0):
colnames(38081): FCAImmP7179369-AAACCTGAGCCCAATT FCAImmP7179369-AAACCTGAGCCTATGT ...
  Human_colon_16S7985397-TTTGCGCAGAGCCCAA Human_colon_16S7985397-TTTGGTTAGTACGCGA
colData names(14): Sample donor ... cell types umap_density_Age
reducedDimNames(1): MOFA
altExpNames(0):

Make Milo object

milo <- Milo(sce)
milo
class: Milo 
dim: 33694 38081 
metadata(0):
assays(2): counts logcounts
rownames(33694): TSPAN6 TNMD ... RP11-107E5.4 RP11-299P2.2
rowData names(0):
colnames(38081): FCAImmP7179369-AAACCTGAGCCCAATT
  FCAImmP7179369-AAACCTGAGCCTATGT ...
  Human_colon_16S7985397-TTTGCGCAGAGCCCAA
  Human_colon_16S7985397-TTTGGTTAGTACGCGA
colData names(14): Sample donor ... cell types umap_density_Age
reducedDimNames(1): MOFA
altExpNames(0):
neighbourhoods dimensions(1): 0
neighbourhoodCounts dimensions(2): 1 1
neighbourDistances dimensions(2): 1 1
graph names(0):
neighbourhoodIndex names(1): 0
object.size(milo)
13060216 bytes

Build KNN graph

For now I use scran function instead of buildGraph from package because it’s very slow

## Rename MOFA dim reduction as PCA so buildGraph can find it
reducedDim(milo, "PCA") <- reducedDim(milo)
# 
# library(BiocNeighbors)
# library(BiocParallel)
# milo <- buildGraph(milo, k = 30)

knn_graph <- buildKNNGraph(reducedDim(milo, "MOFA"), k=50, d=NA, transposed=TRUE)
miloR::graph(milo) <- knn_graph

Run umap

umap_th <- uwot::umap(reducedDim(milo, "MOFA"), n_neighbors=50 )
reducedDim(milo, 'UMAP') <- umap_th
scater::plotUMAP(milo, colour_by="Age", point_size=0.5, point_alpha=0.5) +
  facet_wrap('colour_by')

Test for differential abundance by age

Use Age as ordinal variable w all ages

knn_graph <- buildKNNGraph(reducedDim(milo, "MOFA"), k=50, d=NA, transposed=TRUE)
miloR::graph(milo) <- knn_graph

Run umap

umap_th <- uwot::umap(reducedDim(milo, "MOFA"), n_neighbors=50, verbose=TRUE)
reducedDim(milo, 'UMAP') <- umap_th
scater::plotUMAP(milo, colour_by="Age", point_size=0.5, point_alpha=0.5) +
  facet_wrap('colour_by')

Sample neighborhoods with refined sampling scheme

# milo@neighbourhoods <- list()
system.time(milo <- makeNeighbourhoods(milo, prop=0.1, k = 50, d=5, refined = TRUE, reduced_dims = "PCA", seed = 43))
Checking valid object
   user  system elapsed 
 62.178   1.181  63.665 
plotNeighborhoodSizeHist(milo, bins=100)

Make model matrix for testing. I use Age as an ordinal variable for testing.

th.model
                  (Intercept) Age
F21_TH_45P                  1   9
F22_TH_TOT                  1   3
F23_TH_45P                  1   5
F29_TH_45P                  1  10
F29_TH_45P_5GEX             1  10
F30_TH_45P                  1   8
F30_TH_45P_5GEX             1   8
F38_TH_45P                  1   7
F38_TH_45P_5GEX             1   7
F41_TH_45P                  1   9
F41_TH_45P_5GEX             1   9
F45_TH_45P                  1   6
F45_TH_45P_5GEX             1   6
F64_TH_TOT_5GEX_1           1   5
F64_TH_TOT_5GEX_2           1   5
C34_TH_TOT_5GEX             1   3
C40_TH_TOT_1                1   1
C40_TH_TOT_2                1   1
C41_TH_TOT_1                1   2
C41_TH_TOT_2                1   2
F74_TH_TOT_5GEX_1           1   4
F74_TH_TOT_5GEX_2           1   4
attr(,"assign")
[1] 0 1
milo <- countCells(milo, 
                   data = data.frame(colData(milo)[,c("Sample","Age")]),
                   samples = "Sample")
Checking data validity
Setting up matrix with 3544 neighbourhoods
Counting cells in neighbourhoods
nh_counts <- milo@neighbourhoodCounts

milo_res <- testNeighbourhoods(milo, design = ~ Age, data = th.meta)
Performing spatial FDR correction
hist(milo_res$PValue)

milo_res %>%
  ggplot(aes(PValue, SpatialFDR)) + geom_point() +
  geom_abline(linetype=2) 


milo_res %>%
  ggplot(aes(SpatialFDR)) + geom_histogram()


milo_res %>%
  mutate(is_sig=ifelse(SpatialFDR < 0.05, TRUE, FALSE)) %>%
  ggplot(aes(logFC, -log10(SpatialFDR), color=is_sig)) +
  geom_point(alpha=0.2)

plotMiloReducedDim(milo, milo_results = milo_res, filter_alpha = NULL)

plotMiloReducedDim(milo, milo_results = milo_res, filter_alpha = 0.1)

Perhaps the most interesting part is that proliferative double positive T cells seem to be divided in 2 groups, those more enriched in early stages and late stages. What are the biological differences between these?

Compare enrichment and pseudotime ordering

LS0tCnRpdGxlOiAiTWlsbyBvbiBodW1hbiB0aHltdXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoc2NyYW4pCgpkZXZ0b29sczo6bG9hZF9hbGwoIn4vbWlsb1IvIikKCiMjIFNldHVwIHRvIHVzZSBweXRob24gaW4gUm1kCmxpYnJhcnkocmV0aWN1bGF0ZSkKcmV0aWN1bGF0ZTo6dXNlX2NvbmRhZW52KCJlbW1hX2VudiIpCgp0aGVtZV9kaW1yZWQgPC0gZnVuY3Rpb24oIC4uLiApewogIHRoZW1lKGF4aXMudGlja3MgPWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSksIC4uLiApCiAgfQpgYGAKCmBgYHtweXRob24sIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0KaW1wb3J0IHNjYW5weSBhcyBzYwppbXBvcnQgcGFuZGFzIGFzIHBkCmltcG9ydCBudW1weSBhcyBucAojIyBUbyBzaG93IHBsb3RzIGlubGluZQppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0CmltcG9ydCBzeXMsb3MKcGx0LnN3aXRjaF9iYWNrZW5kKCdhZ2cnKQpzeXMucGF0aC5pbnNlcnQoMSwgJy9uZnMvdGVhbTIwNS9lZDYvYmluL3RoQVRBQy9wcmVwcm9jZXNzX3V0aWxzLycpCmltcG9ydCBhdGFjX3V0aWxzIAppbXBvcnQgc2NpcHkuc3BhcnNlCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAoKIyBzYy5fc2V0dGluZ3MuU2NhbnB5Q29uZmlnLmZpZ2RpciA9IFBhdGgoci5vdXRkaXIpCmBgYAoKCiMjIyBMb2FkIGRhdGEKCkxvYWQgYW5uZGF0YSBvYmplY3QsIGRvd25sb2FkZWQgZnJvbSBbaGVyZV0oaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZC8zNTcyNDIyIy5Yc1kyaDVOS2hRSSkgZm9sbG93aW5nIHRoZSBsaW5rIGZyb20gW1BhcmsgZXQgYWwuIDIwMjBdKDEwLjExMjYvc2NpZW5jZS5hYXkzMjI0KQoKYGBge3B5dGhvbiwgZWNobz1UUlVFLCBldmFsPUZBTFNFfQpybmFfYWRhdGEgPSBzYy5yZWFkX2g1YWQoIi9uZnMvdGVhbTIwNS9lZDYvZGF0YS9QYXJrX3NjUk5Bc2VxL0hUQTA4LnYwMS5BMDYuU2NpZW5jZV9odW1hbl90Y2VsbHMucmF3Lmg1YWQiKQpybmFfYWRhdGEuWCA9IHJuYV9hZGF0YS5yYXcuWApybmFfYWRhdGEuWCA9IHNjaXB5LnNwYXJzZS5jc2NfbWF0cml4KHJuYV9hZGF0YS5YKQpgYGAKCkxvYWQgTU9GQSBwcm9qZWN0aW9uLCB0byB1c2UgYXMgcmVkdWNlZCBkaW1lbnNpb25hbGl0eSBvYmplY3QKCmBgYHtyLCBlY2hvPVRSVUUsIGV2YWw9RkFMU0V9Cm1vZmFfZGltcyA8LSByZWFkLmNzdigiL25mcy90ZWFtMjA1L2VkNi9kYXRhL3RoeW11c19kYXRhL3RoeW11c19NT0ZBX3Byb2plY3Rpb24uY3N2IikgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsIikKCiMjIEZpbHRlciBqdXN0IHRoZSBzY1JOQS1zZXEgY2VsbHMgYW5kIGZhY3RvcnMgNCBrbm4gZ3Jhb2ggY29uc3RydWNpdG9uCm1vZmFfZGltcyA8LSBhcy5tYXRyaXgobW9mYV9kaW1zW3Jvd25hbWVzKHB5JHJuYV9hZGF0YSRvYnMpLDE6NV0pCmBgYAoKQ29udmVydCBgYW5uZGF0YWAgdG8gYFNpbmdsZUNlbGxFeHBlcmltZW50YCBvYmplY3QgCgpgYGB7ciwgZWNobz1UUlVFLCBldmFsPUZBTFNFfQphZGF0YSA8LSBweSRybmFfYWRhdGEKY250IDwtIHQoYWRhdGEkWCkKcm93bmFtZXMoY250KSA8LSBhZGF0YSR2YXJfbmFtZXMkdG9fbGlzdCgpCmNvbG5hbWVzKGNudCkgPC0gYWRhdGEkb2JzX25hbWVzJHRvX2xpc3QoKQpsb2dDbnQgPC0gbG9nMihjbnQgKyAxKQpwY2EgPC0gcHJjb21wKHQodngpKQoKIyBDcmVhdGUgdGhlIFNpbmdsZUNlbGxFeHBlcmltZW50IG9iamVjdApzY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQoYXNzYXk9bGlzdChjb3VudHM9Y250LCBsb2djb3VudHM9bG9nQ250KSwgY29sRGF0YSA9IGFkYXRhJG9icykKCnJlZHVjZWREaW0oc2NlKSA8LSBtb2ZhX2RpbXMKcmVkdWNlZERpbU5hbWVzKHNjZSkgPC0gIk1PRkEiCnNjZQoKIyMgU2F2ZSBTaW5nbGVDZWxsRXhwZXJpbWVudApzYXZlUkRTKHNjZSwgIi9uZnMvdGVhbTIwNS9lZDYvZGF0YS9QYXJrX3NjUk5Bc2VxL0hUQTA4LnYwMS5BMDYuU2NpZW5jZV9odW1hbl90Y2VsbHMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQuUkRTIikKYGBgCgpgYGB7cn0Kc2NlIDwtIHJlYWRSRFMoIn4vRG93bmxvYWRzL0hUQTA4LnYwMS5BMDYuU2NpZW5jZV9odW1hbl90Y2VsbHMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQuUkRTIikKc2NlCm9iamVjdC5zaXplKHNjZSkKYGBgCgpDZWxscyBpbiBkaWZmZXJlbnQgc2FtcGxlcyB3aGVyZSBzb3J0ZWQgdXNpbmcgZGlmZmVyZW50IEZBQ1MgZ2F0ZXMsIHdoaWNoIGFmZmVjdCBzaWduaWZpY2FudGx5IHRoZSBjZWxsIHR5cGUgY29tcG9zaXRpb24gb2YgZGlmZmVyZW50IHNhbXBsZXMuIFRvIHNpbXBsaWZ5IGludGVycHJldGF0aW9uIG9mIERBIGFuYWx5c2lzLCBJIHJldGFpbiBvbmx5IHNhbXBsZXMgdGhhdCB3aGVyZSBvYnRhaW5lZCBmcm9tIHRvdGFsIHRpc3N1ZSBhbmQgQ0Q0NSsgY2VsbHMsIHdoaWNoIGhhdmUgYSBzaW1pbGFyIGNlbGwgdHlwZSBjb21wb3NpdGlvbi4KCmBgYHtyfQpjb2xEYXRhKHNjZSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGdncGxvdChhZXMoU2FtcGxlLCBmaWxsPWNlbGwudHlwZXMpKSArCiAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiKSArCiAgY29vcmRfZmxpcCgpICsKICBmYWNldF9ncmlkKHNvcnR+Liwgc2NhbGVzPSJmcmVlX3kiLCBzcGFjZT0iZnJlZSIpCmBgYAoKCgpgYGB7cn0Ka2VlcF9jZWxscyA8LSB3aGljaChzY2Ukc29ydCAlaW4lIGMoIlRPVCIsICI0NVAiKSkKc2NlIDwtIHNjZVssa2VlcF9jZWxsc10Kc2NlCmBgYAoKCk1ha2UgTWlsbyBvYmplY3QKYGBge3J9Cm1pbG8gPC0gTWlsbyhzY2UpCm1pbG8Kb2JqZWN0LnNpemUobWlsbykKYGBgCgpgYGB7cn0KdGVzdF9uaF9zaXplIDwtIGZ1bmN0aW9uKG0sIHByb3AsIGssIGQ9NSl7CiAgIyBtIDwtIG1bLHNhbXBsZShjb2xuYW1lcyhtKSwgc2l6ZSA9IG5fY2VsbHMpXQogIG0gPC0gYnVpbGRHcmFwaChtLCBrID0gaywgZCA9IGQpCiAgcmVmaW5lZF9uaCA8LSBuZWlnaGJvdXJob29kcyhtYWtlTmVpZ2hib3VyaG9vZHMobSwgcHJvcD1wcm9wLCBrPWssIGQ9ZCwgcmVmaW5lZCA9IFRSVUUsIHNlZWQ9NDIpKQogIHJldHVybihzYXBwbHkocmVmaW5lZF9uaCwgbGVuZ3RoKSkKfQoKcmVkdWNlZERpbShtaWxvLCAiUENBIikgPC0gcmVkdWNlZERpbShtaWxvKQprX3ZlYyA8LSBzZXEoMTAsNTAsIGJ5PTEwKQpuaF9zaXplc190aCA8LSBsYXBwbHkoa192ZWMsIGZ1bmN0aW9uKHgpIHRlc3Rfbmhfc2l6ZShtaWxvLCBwcm9wID0gMC4xLCB4LCBkPTUpKQpuaF9zaXplc190aF8yIDwtIGxhcHBseShjKDYwLDcwLDgwKSwgZnVuY3Rpb24oeCkgdGVzdF9uaF9zaXplKG1pbG8sIHByb3AgPSAwLjEsIHgsIGQ9NSkpCm5oX3NpemVzX3RoIDwtIHNldF9uYW1lcyhuaF9zaXplc190aCwgcGFzdGUwKCdrJywga192ZWMpKQpuaF9zaXplc190aF8yIDwtIHNldF9uYW1lcyhuaF9zaXplc190aF8yLCBwYXN0ZTAoJ2snLCBjKDYwLDcwLDgwKSkpCgppbWFwKGMobmhfc2l6ZXNfdGgsIG5oX3NpemVzX3RoXzIpLCB+IGRhdGEuZnJhbWUobmhfc2l6ZT0ueCwgaz0ueSkpICU+JQogIHJlZHVjZShiaW5kX3Jvd3MpICU+JQogIGdyb3VwX2J5KGspICU+JQogIG11dGF0ZShuX25laWdoYm9yaG9vZHM9bigpKSAlPiUKICBnZ3Bsb3QoYWVzKG5fbmVpZ2hib3Job29kcywgbmhfc2l6ZSwgZmlsbD1rKSkgKyAKICBnZW9tX2JveHBsb3QodmFyd2lkdGggPSBUUlVFKSArCiAgIyBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbj0iaWRlbnRpdHkiLCBhbHBoYT0wLjYsIGJpbnM9NTApICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpCmBgYAoKIyMgQnVpbGQgS05OIGdyYXBoCgpGb3Igbm93IEkgdXNlIHNjcmFuIGZ1bmN0aW9uIGluc3RlYWQgb2YgYGJ1aWxkR3JhcGhgIGZyb20gcGFja2FnZSBiZWNhdXNlIGl0J3MgdmVyeSBzbG93CgpgYGB7cn0KIyMgUmVuYW1lIE1PRkEgZGltIHJlZHVjdGlvbiBhcyBQQ0Egc28gYnVpbGRHcmFwaCBjYW4gZmluZCBpdApyZWR1Y2VkRGltKG1pbG8sICJQQ0EiKSA8LSByZWR1Y2VkRGltKG1pbG8pCiMgCiMgbGlicmFyeShCaW9jTmVpZ2hib3JzKQojIGxpYnJhcnkoQmlvY1BhcmFsbGVsKQojIG1pbG8gPC0gYnVpbGRHcmFwaChtaWxvLCBrID0gMzApCgprbm5fZ3JhcGggPC0gYnVpbGRLTk5HcmFwaChyZWR1Y2VkRGltKG1pbG8sICJNT0ZBIiksIGs9NTAsIGQ9TkEsIHRyYW5zcG9zZWQ9VFJVRSkKbWlsb1I6OmdyYXBoKG1pbG8pIDwtIGtubl9ncmFwaApgYGAKClJ1biB1bWFwCmBgYHtyfQp1bWFwX3RoIDwtIHV3b3Q6OnVtYXAocmVkdWNlZERpbShtaWxvLCAiTU9GQSIpLCBuX25laWdoYm9ycz01MCApCnJlZHVjZWREaW0obWlsbywgJ1VNQVAnKSA8LSB1bWFwX3RoCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0Kc2NhdGVyOjpwbG90VU1BUChtaWxvLCBjb2xvdXJfYnk9IkFnZSIsIHBvaW50X3NpemU9MC41LCBwb2ludF9hbHBoYT0wLjUpICsKICBmYWNldF93cmFwKCdjb2xvdXJfYnknKQpgYGAKCiMjIFRlc3QgZm9yIGRpZmZlcmVudGlhbCBhYnVuZGFuY2UgYnkgYWdlCgo8IS0tICMjIyBTaW1wbGUgY2FzZTogY29tcGFyZSBmaXJzdCB0aW1lIHBvaW50IHdpdGggbGFzdCBvbmUgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBzbWFsbF9taWxvIDwtIG1pbG9bLHdoaWNoKG1pbG8kQWdlICVpbiUgYygnN3cnLCcxN3cnKSldIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIGtubl9ncmFwaCA8LSBidWlsZEtOTkdyYXBoKHJlZHVjZWREaW0oc21hbGxfbWlsbywgIk1PRkEiKSwgaz0zMCwgZD1OQSwgdHJhbnNwb3NlZD1UUlVFKSAtLT4KPCEtLSBtaWxvUjo6Z3JhcGgoc21hbGxfbWlsbykgPC0ga25uX2dyYXBoIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gUnVuIHVtYXAgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIHNtYWxsX3VtYXBfdGggPC0gdXdvdDo6dW1hcChyZWR1Y2VkRGltKHNtYWxsX21pbG8sICJNT0ZBIiksIG5fbmVpZ2hib3JzPTMwICkgLS0+CjwhLS0gcmVkdWNlZERpbShzbWFsbF9taWxvLCAnVU1BUCcpIDwtIHNtYWxsX3VtYXBfdGggLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9IC0tPgo8IS0tIHNjYXRlcjo6cGxvdFVNQVAoc21hbGxfbWlsbywgY29sb3VyX2J5PSJBZ2UiLCBwb2ludF9zaXplPTAuNSwgcG9pbnRfYWxwaGE9MC41KSArIC0tPgo8IS0tICAgZmFjZXRfd3JhcCgnY29sb3VyX2J5JykgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBTYW1wbGUgbmVpZ2hib3Job29kcyB3aXRoIHJlZmluZWQgc2FtcGxpbmcgc2NoZW1lIC0tPgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gIyBtaWxvQG5laWdoYm91cmhvb2RzIDwtIGxpc3QoKSAtLT4KPCEtLSBzbWFsbF9taWxvIDwtIG1ha2VOZWlnaGJvdXJob29kcyhzbWFsbF9taWxvLCBwcm9wPTAuMSwgayA9IDMwLCBkPTUsIHJlZmluZWQgPSBUUlVFLCByZWR1Y2VkX2RpbXMgPSAiUENBIiwgc2VlZCA9IDEwMCkgLS0+CgoKPCEtLSBwbG90TmVpZ2hib3Job29kU2l6ZUhpc3Qoc21hbGxfbWlsbywgYmlucz0xMDApIC0tPgoKPCEtLSBgYGAgLS0+CgoKPCEtLSBNYWtlIG1vZGVsIG1hdHJpeCBmb3IgdGVzdGluZy4gSSB1c2UgQWdlIGFzIGFuIG9yZGluYWwgdmFyaWFibGUgZm9yIHRlc3RpbmcuIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSB0aC5tZXRhIDwtIGRhdGEuZnJhbWUoY29sRGF0YShzbWFsbF9taWxvKVssYygiU2FtcGxlIiwiQWdlIildKSAgLS0+CjwhLS0gdGgubWV0YSRBZ2UgPC0gb3JkZXJlZCh0aC5tZXRhJEFnZSwgbGV2ZWxzPWMoJzd3JywnMTd3JykpIC0tPgo8IS0tIHRoLm1ldGEgPC0gLS0+CjwhLS0gICBkaXN0aW5jdCh0aC5tZXRhKSAlPiUgLS0+CjwhLS0gICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBzZWxlY3QoU2FtcGxlLCBBZ2UpICU+JSAtLT4KPCEtLSAgIGNvbHVtbl90b19yb3duYW1lcygiU2FtcGxlIikgLS0+Cgo8IS0tIHRoLm1ldGEgJT4lIC0tPgo8IS0tICAgIyBmaWx0ZXIoQWdlPT0iMTZ3IikgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKEFnZSkpICsgZ2VvbV9iYXIoKSAtLT4KCjwhLS0gdGgubW9kZWwgPC0gbW9kZWwubWF0cml4KH4gIEFnZSwgZGF0YT10aC5tZXRhKSAtLT4KPCEtLSB0aC5tb2RlbCAtLT4KPCEtLSBgYGAgLS0+CgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gc21hbGxfbWlsbyA8LSBjb3VudENlbGxzKHNtYWxsX21pbG8sICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEuZnJhbWUoY29sRGF0YShzbWFsbF9taWxvKVssYygiU2FtcGxlIiwiQWdlIildKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgIHNhbXBsZXMgPSAiU2FtcGxlIikgLS0+Cgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIGdyYXBoX3NwYXRpYWxGRFIgPC0gZnVuY3Rpb24obmVpZ2hib3Job29kcywgZ3JhcGgsIHB2YWx1ZXMsIGNvbm5lY3Rpdml0eT0ndmVydGV4JywgcGNhPU5VTEwpeyAtLT4KPCEtLSAgICMgaW5wdXQgYSBzZXQgb2YgbmVpZ2hib3Job29kcyBhcyBhIGxpc3Qgb2YgZ3JhcGggdmVydGljZXMgLS0+CjwhLS0gICAjIHRoZSBpbnB1dCBncmFwaCBhbmQgdGhlIHVuYWRqdXN0ZWQgR0xNIHAtdmFsdWVzIC0tPgo8IS0tICAgIycgbmVpZ2hib3Job29kczogbGlzdCBvZiB2ZXJ0aWNlcyBhbmQgdGhlaXIgcmVzcGVjdGl2ZSBuZWlnaGJvcmhvb2RzIC0tPgo8IS0tICAgIycgZ3JhcGg6IGlucHV0IGtOTiBncmFwaCAtLT4KPCEtLSAgICMnIHB2YWx1ZXM6IGEgdmVjdG9yIG9mIHB2YWx1ZXMgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlIG5laWdoYm9yaG9vZCBpbmRpY2VzIC0tPgo8IS0tICAgIycgY29ubmVjdGl2aXR5OiBjaGFyYWN0ZXIgLSBlZGdlIG9yIHZlcnRleCB0byBjYWxjdWxhdGUgbmVpZ2hib3Job29kIGNvbm5lY3Rpdml0eSBvciBkaXN0YW5jZSB0byB1c2UgYXZlcmFnZSBFdWNsaWRlYW4gZGlzdGFuY2UgLS0+CjwhLS0gICAjJyBwY2E6IG1hdHJpeCBvZiBQQ3MgdG8gY2FsY3VsYXRlIEV1Y2xpZGVhbiBkaXN0YW5jZXMsIG9ubHkgcmVxdWlyZWQgd2hlbiBjb25uZWN0aXZpdHkgPT0gZGlzdGFuY2UgLS0+CjwhLS0gICAjIERpc2NhcmRpbmcgTkEgcHZhbHVlcy4gLS0+CjwhLS0gICBoYXNwdmFsIDwtICFpcy5uYShwdmFsdWVzKSAtLT4KPCEtLSAgIGlmICghYWxsKGhhc3B2YWwpKSB7IC0tPgo8IS0tICAgICAgIGNvb3JkcyA8LSBjb29yZHNbaGFzcHZhbCwgLCBkcm9wPUZBTFNFXSAtLT4KPCEtLSAgICAgICBwdmFsdWVzIDwtIHB2YWx1ZXNbaGFzcHZhbF0gLS0+CjwhLS0gICB9IC0tPgoKPCEtLSAgICMgZGVmaW5lIHRoZSBzdWJncmFwaCBmb3IgZWFjaCBuZWlnaGJvcmhvb2QgdGhlbiBjYWxjdWxhdGUgdGhlIHZlcnRleCBjb25uZWN0aXZpdHkgZm9yIGVhY2ggLS0+CjwhLS0gICAjIHRoaXMgbGF0dGVyIGNvbXB1dGF0aW9uIGlzIHF1aXRlIHNsb3cgLSBjYW4gaXQgYmUgc3BlZCB1cD8gLS0+CjwhLS0gICBzdWJncmFwaHMgPC0gbGFwcGx5KDE6bGVuZ3RoKG5laWdoYm9yaG9vZHNbaGFzcHZhbF0pLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOPWZ1bmN0aW9uKFgpIGluZHVjZWRfc3ViZ3JhcGgoZ3JhcGgsIG5laWdoYm9yaG9vZHNbaGFzcHZhbF1bW1hdXSkpIC0tPgo8IS0tICAgIyBub3cgbG9vcCBvdmVyIHRoZXNlIHN1Yi1ncmFwaHMgdG8gY2FsY3VsYXRlIHRoZSBjb25uZWN0aXZpdHkgLSB0aGlzIHNlZW1zIGEgbGl0dGxlIHNsb3cuLi4gLS0+CjwhLS0gICBpZihjb25uZWN0aXZpdHkgPT0gInZlcnRleCIpeyAtLT4KPCEtLSAgICAgdC5jb25uZWN0IDwtIGxhcHBseShzdWJncmFwaHMsIEZVTj1mdW5jdGlvbihFRykgdmVydGV4X2Nvbm5lY3Rpdml0eShFRykpIC0tPgo8IS0tICAgfSBlbHNlIGlmKGNvbm5lY3Rpdml0eSA9PSAiZWRnZSIpeyAtLT4KPCEtLSAgICAgdC5jb25uZWN0IDwtIGxhcHBseShzdWJncmFwaHMsIEZVTj1mdW5jdGlvbihFRykgZWRnZV9jb25uZWN0aXZpdHkoRUcpKSAtLT4KPCEtLSAgIH0gZWxzZSBpZihjb25uZWN0aXZpdHkgPT0gImRpc3RhbmNlIil7IC0tPgo8IS0tICAgICBpZighaXMubnVsbChwY2EpKXsgLS0+CjwhLS0gICAgICAgdC5jb25uZWN0IDwtIGxhcHBseSgxOmxlbmd0aChuZWlnaGJvcmhvb2RzW2hhc3B2YWxdKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgRlVOPWZ1bmN0aW9uKFBHKSB7IC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5wY3MgPC0gcGNhW25laWdoYm9yaG9vZHNbaGFzcHZhbF1bW1BHXV0sIF0gLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICB4LmV1Y2xpZCA8LSBhcy5tYXRyaXgoZGlzdCh4LnBjcykpIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5kaXN0ZGVucyA8LSAxL21lYW4oeC5ldWNsaWRbbG93ZXIudHJpKHguZXVjbGlkLCBkaWFnPUZBTFNFKV0pIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybih4LmRpc3RkZW5zKX0pIC0tPgo8IS0tICAgICB9IGVsc2V7IC0tPgo8IS0tICAgICAgIHN0b3AoIkEgbWF0cml4IG9mIFBDcyBpcyByZXF1aXJlZCB0byBjYWxjdWxhdGUgZGlzdGFuY2VzIikgICAtLT4KPCEtLSAgICAgfSAtLT4KPCEtLSAgIH1lbHNleyAtLT4KPCEtLSAgICAgc3RvcCgiY29ubmVjdGl2aXR5IG9wdGlvbiBub3QgcmVjb2duaXNlZCAtIG11c3QgYmUgZWl0aGVyIGVkZ2UsIHZlcnRleCBvciBkaXN0YW5jZSIpIC0tPgo8IS0tICAgfSAtLT4KCjwhLS0gICAjIHVzZSAxL2Nvbm5lY3Rpdml0eSBhcyB0aGUgd2VpZ2h0aW5nIGZvciB0aGUgd2VpZ2h0ZWQgQkggYWRqdXN0bWVudCBmcm9tIEN5ZGFyIC0tPgo8IS0tICAgdyA8LSAxL3VubGlzdCh0LmNvbm5lY3QpIC0tPgo8IS0tICAgd1tpcy5pbmZpbml0ZSh3KV0gPC0gMCAtLT4KCjwhLS0gICAjIENvbXB1dGluZyBhIGRlbnNpdHktd2VpZ2h0ZWQgcS12YWx1ZS4gLS0+CjwhLS0gICBvIDwtIG9yZGVyKHB2YWx1ZXMpIC0tPgo8IS0tICAgcHZhbHVlcyA8LSBwdmFsdWVzW29dIC0tPgo8IS0tICAgdyA8LSB3W29dIC0tPgo8IS0tICAgYWRqcCA8LSBudW1lcmljKGxlbmd0aChvKSkgLS0+CjwhLS0gICBhZGpwW29dIDwtIHJldihjdW1taW4ocmV2KHN1bSh3KSpwdmFsdWVzL2N1bXN1bSh3KSkpKSAtLT4KPCEtLSAgIGFkanAgPC0gcG1pbihhZGpwLCAxKSAtLT4KPCEtLSAgIGlmICghYWxsKGhhc3B2YWwpKSB7IC0tPgo8IS0tICAgICByZWZwIDwtIHJlcChOQV9yZWFsXywgbGVuZ3RoKGhhc3B2YWwpKSAtLT4KPCEtLSAgICAgcmVmcFtoYXNwdmFsXSA8LSBhZGpwIC0tPgo8IS0tICAgICBhZGpwIDwtIHJlZnAgLS0+CjwhLS0gICAgIH0gLS0+CjwhLS0gICByZXR1cm4oYWRqcCkgLS0+CjwhLS0gfSAtLT4KCjwhLS0gIyB0ZXN0UUxGIDwtIGZ1bmN0aW9uKGdyYXBoLCBuaF9jb3VudHMsIHRoLm1vZGVsLCBjb25uZWN0aXZpdHk9J2VkZ2UnLCBwY2E9TlVMTCl7IC0tPgo8IS0tIG5oX2NvdW50cyA8LSBzbWFsbF9taWxvQG5laWdoYm91cmhvb2RDb3VudHMgLS0+Cgo8IS0tIGRnZSA8LSBER0VMaXN0KG5oX2NvdW50c1ssIHJvd25hbWVzKHRoLm1vZGVsKV0sIGxpYi5zaXplPWxvZyhjb2xTdW1zKG5oX2NvdW50cykpKSAtLT4KPCEtLSBkZ2UgPC0gZXN0aW1hdGVEaXNwKGRnZSwgdGgubW9kZWwpIC0tPgoKPCEtLSBmaXQgPC0gZ2xtUUxGaXQoZGdlLCB0aC5tb2RlbCwgcm9idXN0PVRSVUUpIC0tPgo8IS0tICMgc2ltMi5jb250cmFzdCA8LSBtYWtlQ29udHJhc3RzKENvbmRpdGlvbkEgLSBDb25kaXRpb25CLCBsZXZlbHM9dGgubW9kZWwpIC0tPgo8IS0tICMgICBzaW0yLnJlcyA8LSBnbG1RTEZUZXN0KHNpbTIuZml0LCBjb250cmFzdD1zaW0yLmNvbnRyYXN0KSAtLT4KPCEtLSBtaWxvX3JlcyA8LSBhcy5kYXRhLmZyYW1lKHRvcFRhZ3MoZ2xtUUxGVGVzdChmaXQsIGNvZWY9MSksIHNvcnQuYnk9J25vbmUnLCBuPUluZikpIC0tPgo8IS0tIG1pbG9fcmVzJFNpZyA8LSBhcy5mYWN0b3IoYXMubnVtZXJpYyhtaWxvX3JlcyRGRFIgPD0gMC4wNSkpIC0tPgo8IS0tIG1pbG9fcmVzJE5laWdoYm91cmhvb2QgPC0gYXMubnVtZXJpYyhyb3duYW1lcyhtaWxvX3JlcykpIC0tPgoKPCEtLSBzaW0yLnNwYXRpYWxmZHIgPC0gZ3JhcGhfc3BhdGlhbEZEUihuZWlnaGJvcmhvb2RzPXNtYWxsX21pbG9AbmVpZ2hib3VyaG9vZHMsICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaD1zbWFsbF9taWxvQGdyYXBoW1siZ3JhcGgiXV0sIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpdml0eT0iZGlzdGFuY2UiLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlcz1taWxvX3JlcyRQVmFsdWUsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBjYT1yZWR1Y2VkRGltKG1pbG8sIk1PRkEiKSAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIC0tPgo8IS0tIGBgYCAtLT4KPCEtLSBgYGB7cn0gLS0+CjwhLS0gbWlsb19yZXNfZGYgPC0gZGF0YS5mcmFtZShWZXJ0ZXg9bmFtZXMoc21hbGxfbWlsb0BuZWlnaGJvdXJob29kcyksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgcD1taWxvX3JlcyRQVmFsdWUsICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkanA9c2ltMi5zcGF0aWFsZmRyLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dGQz1taWxvX3JlcyRsb2dGQywgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRqcF9mZHI9bWlsb19yZXMkRkRSLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICBTaWc9bWlsb19yZXMkU2lnIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAtLT4KCgo8IS0tIG1pbG9fcmVzX2RmICU+JSAtLT4KPCEtLSAgIG11dGF0ZShpc19zaWc9aWZlbHNlKGFkanAgPCAwLjEsIFRSVUUsIEZBTFNFKSkgJT4lIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhsb2dGQywgLWxvZzEwKGFkanApLCBjb2xvcj1pc19zaWcpKSArIC0tPgo8IS0tICAgZ2VvbV9wb2ludChzaXplPTAuMSkgLS0+CjwhLS0gYGBgIC0tPgoKCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAjIGNvbERhdGEoc21hbGxfbWlsbykgPC0gY29sRGF0YShzY2VbLHdoaWNoKHNjZSRBZ2UgJWluJSBjKCc3dycsJzE3dycpKV0pIC0tPgo8IS0tIGNvbERhdGEoc21hbGxfbWlsbylbIlZlcnRleCJdIDwtIGFzLmNoYXJhY3RlcihWKGdyYXBoKHNtYWxsX21pbG8pKSkgLS0+CjwhLS0gY29sZGF0YV9kZiA8LSAtLT4KPCEtLSAgIFNpbmdsZUNlbGxFeHBlcmltZW50Ojpjb2xEYXRhKHNtYWxsX21pbG8pICU+JSAtLT4KPCEtLSAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgLS0+CjwhLS0gICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBsZWZ0X2pvaW4obWlsb19yZXNfZGYpICAtLT4KCjwhLS0gY29sZGF0YV9kZiAtLT4KPCEtLSAjIGNvbERhdGEoc21hbGxfbWlsbylbd2hpY2goc21hbGxfbWlsbyRWZXJ0ZXg9PSI4NTAiKSxdIC0tPgo8IS0tICMgICBtaWxvX3Jlc19kZiAlPiUgLS0+CjwhLS0gIyAgICAgZmlsdGVyKGxvZ0ZDID4gMCkgJT4lIHB1bGwoVmVydGV4KSAtLT4KCjwhLS0gIyBjb2xuYW1lcyhtaWxvX3Jlc19kZikgJWluJSBjb2xuYW1lcyhjb2xEYXRhKHNtYWxsX21pbG8pKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBjb2xkYXRhX2RmIDwtIGJpbmRfY29scyhjb2xkYXRhX2RmLCBkYXRhLmZyYW1lKHJlZHVjZWREaW0oc21hbGxfbWlsbywgJ1VNQVAnKSkpICAtLT4KPCEtLSBgYGAgLS0+CgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gIyBwbF9kZiA8LSBzY2F0ZXI6OnBsb3RSZWR1Y2VkRGltKHNtYWxsX21pbG8sZGltcmVkPSJVTUFQIiwgY29sb3VyX2J5ID0gImxvZ0ZDIiwgKSRkYXRhICAgLS0+Cgo8IS0tIGNvbGRhdGFfZGYgJT4lIC0tPgo8IS0tICAgYXJyYW5nZSgtIGxvZ0ZDKSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKFgxLFgyKSkgKyAgLS0+CjwhLS0gICBnZW9tX3BvaW50KHNpemU9MC4yLCBjb2xvcj0iZ3JleSIsIGFscGhhPTAuNSkgKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoZGF0YT0uICU+JSBmaWx0ZXIoIWlzLm5hKGxvZ0ZDKSksIGFlcyhjb2xvcj1sb2dGQyksIHNpemU9MSkgKyAtLT4KPCEtLSAgICMgc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZD0wLCBoaWdoID0gInJlZCIsIGxvdz0iYmx1ZSIpICsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uPSJtYWdtYSIpICsgLS0+CjwhLS0gICB0aGVtZV9kaW1yZWQoKSAtLT4KCjwhLS0gY29sZGF0YV9kZiAlPiUgLS0+CjwhLS0gICBhcnJhbmdlKC0gbG9nMTAoYWRqcCkpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoWDEsWDIpKSArICAtLT4KPCEtLSAgIGdlb21fcG9pbnQoc2l6ZT0wLjIsIGNvbG9yPSJncmV5IiwgYWxwaGE9MC41KSArIC0tPgo8IS0tICAgZ2VvbV9wb2ludChkYXRhPS4gJT4lIGZpbHRlcighaXMubmEoYWRqcCkpLCBhZXMoY29sb3I9LWxvZzEwKGFkanApKSkgKyAtLT4KPCEtLSAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsgLS0+CjwhLS0gICB0aGVtZV9kaW1yZWQoKSAtLT4KCjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gY29sZGF0YV9kZiAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKFgxLCBYMikpICsgLS0+CjwhLS0gICBnZW9tX3BvaW50KGFlcyhjb2xvcj1BZ2UpLCBzaXplPTAuNSkgLS0+CjwhLS0gYGBgIC0tPgoKIyMjIFVzZSBBZ2UgYXMgb3JkaW5hbCB2YXJpYWJsZSB3IGFsbCBhZ2VzCgpgYGB7cn0Ka25uX2dyYXBoIDwtIGJ1aWxkS05OR3JhcGgocmVkdWNlZERpbShtaWxvLCAiTU9GQSIpLCBrPTUwLCBkPU5BLCB0cmFuc3Bvc2VkPVRSVUUpCm1pbG9SOjpncmFwaChtaWxvKSA8LSBrbm5fZ3JhcGgKYGBgCgpSdW4gdW1hcApgYGB7cn0KdW1hcF90aCA8LSB1d290Ojp1bWFwKHJlZHVjZWREaW0obWlsbywgIk1PRkEiKSwgbl9uZWlnaGJvcnM9NTAsIHZlcmJvc2U9VFJVRSkKcmVkdWNlZERpbShtaWxvLCAnVU1BUCcpIDwtIHVtYXBfdGgKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpzY2F0ZXI6OnBsb3RVTUFQKG1pbG8sIGNvbG91cl9ieT0iQWdlIiwgcG9pbnRfc2l6ZT0wLjUsIHBvaW50X2FscGhhPTAuNSkgKwogIGZhY2V0X3dyYXAoJ2NvbG91cl9ieScpCmBgYAoKU2FtcGxlIG5laWdoYm9yaG9vZHMgd2l0aCByZWZpbmVkIHNhbXBsaW5nIHNjaGVtZQoKYGBge3J9CiMgbWlsb0BuZWlnaGJvdXJob29kcyA8LSBsaXN0KCkKc3lzdGVtLnRpbWUobWlsbyA8LSBtYWtlTmVpZ2hib3VyaG9vZHMobWlsbywgcHJvcD0wLjEsIGsgPSA1MCwgZD01LCByZWZpbmVkID0gVFJVRSwgcmVkdWNlZF9kaW1zID0gIlBDQSIsIHNlZWQgPSA0MykpCgpwbG90TmVpZ2hib3Job29kU2l6ZUhpc3QobWlsbywgYmlucz04MCkKYGBgCgoKTWFrZSBtb2RlbCBtYXRyaXggZm9yIHRlc3RpbmcuIEkgdXNlIEFnZSBhcyBhbiBvcmRpbmFsIHZhcmlhYmxlIGZvciB0ZXN0aW5nLgpgYGB7cn0KdGgubWV0YSA8LSBkYXRhLmZyYW1lKGNvbERhdGEobWlsbylbLGMoIlNhbXBsZSIsIkFnZSIpXSkgCnRoLm1ldGEkQWdlIDwtIG9yZGVyZWQodGgubWV0YSRBZ2UsIGxldmVscz1jKCc3dycsICc4dycsICc5dycsICcxMHcnLCAnMTF3JywgJzEydycsICcxM3cnLCAnMTR3JywgJzE2dycsICcxN3cnKSkKdGgubWV0YSRBZ2UgPC0gYXMubnVtZXJpYyh0aC5tZXRhJEFnZSkKdGgubWV0YSA8LQogIGRpc3RpbmN0KHRoLm1ldGEpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIHNlbGVjdChTYW1wbGUsIEFnZSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGUiKQoKdGgubWV0YSAlPiUKICAjIGZpbHRlcihBZ2U9PSIxNnciKQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2JhcigpCgp0aC5tb2RlbCA8LSBtb2RlbC5tYXRyaXgofiAgQWdlLCBkYXRhPXRoLm1ldGEpCnRoLm1vZGVsCmBgYAoKCmBgYHtyfQptaWxvIDwtIGNvdW50Q2VsbHMobWlsbywgCiAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YS5mcmFtZShjb2xEYXRhKG1pbG8pWyxjKCJTYW1wbGUiLCJBZ2UiKV0pLAogICAgICAgICAgICAgICAgICAgc2FtcGxlcyA9ICJTYW1wbGUiKQoKCmBgYAoKYGBge3J9Cm5oX2NvdW50cyA8LSBtaWxvQG5laWdoYm91cmhvb2RDb3VudHMKCm1pbG9fcmVzIDwtIHRlc3ROZWlnaGJvdXJob29kcyhtaWxvLCBkZXNpZ24gPSB+IEFnZSwgZGF0YSA9IHRoLm1ldGEpCmhpc3QobWlsb19yZXMkUFZhbHVlKQpgYGAKCmBgYHtyfQptaWxvX3JlcyAlPiUKICBnZ3Bsb3QoYWVzKFBWYWx1ZSwgU3BhdGlhbEZEUikpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX2FibGluZShsaW5ldHlwZT0yKSAKCm1pbG9fcmVzICU+JQogIGdncGxvdChhZXMoU3BhdGlhbEZEUikpICsgZ2VvbV9oaXN0b2dyYW0oKQoKbWlsb19yZXMgJT4lCiAgbXV0YXRlKGlzX3NpZz1pZmVsc2UoU3BhdGlhbEZEUiA8IDAuMDUsIFRSVUUsIEZBTFNFKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQywgLWxvZzEwKFNwYXRpYWxGRFIpLCBjb2xvcj1pc19zaWcpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0wLjIpCmBgYAoKYGBge3J9CnBsb3RNaWxvUmVkdWNlZERpbShtaWxvLCBtaWxvX3Jlc3VsdHMgPSBtaWxvX3JlcywgZmlsdGVyX2FscGhhID0gTlVMTCkKcGxvdE1pbG9SZWR1Y2VkRGltKG1pbG8sIG1pbG9fcmVzdWx0cyA9IG1pbG9fcmVzLCBmaWx0ZXJfYWxwaGEgPSAwLjEpIApgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwbG90TWlsb1JlZHVjZWREaW0obWlsbywgbWlsb19yZXN1bHRzID0gbWlsb19yZXMsIHNwbGl0X2J5ID0gImNlbGwgdHlwZXMiLCBmaWx0ZXJfYWxwaGEgPSAwLjEpCmBgYAoKUGVyaGFwcyB0aGUgbW9zdCBpbnRlcmVzdGluZyBwYXJ0IGlzIHRoYXQgcHJvbGlmZXJhdGl2ZSBkb3VibGUgcG9zaXRpdmUgVCBjZWxscyBzZWVtIHRvIGJlIGRpdmlkZWQgaW4gMiBncm91cHMsIHRob3NlIG1vcmUgZW5yaWNoZWQgaW4gZWFybHkgc3RhZ2VzIGFuZCBsYXRlIHN0YWdlcy4gV2hhdCBhcmUgdGhlIGJpb2xvZ2ljYWwgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGVzZT8KCmBgYHtyfQpjZWxsdHlwZS5kZiA8LSBkYXRhLmZyYW1lKG5oSW5kZXg9MTpuY29sKG1pbG8pLCBjZWxsLnR5cGVzPWNvbERhdGEobWlsbylbLCJjZWxsIHR5cGVzIl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgQ0QzPWNvdW50cyhtaWxvKVsiQ0QzRCIsXSwgQ0QyOD1jb3VudHMobWlsbylbIkNEMjgiLF0sIENEND1jb3VudHMobWlsbylbIkNENCIsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBDRDhBPWNvdW50cyhtaWxvKVsiQ0Q4QSIsXSwgQ0Q4Qj1jb3VudHMobWlsbylbIkNEOEIiLF0pCgoKCm1pbG9fcmVzICU+JSAKICBtdXRhdGUobmhJbmRleCA9IHVubGlzdChuZWlnaGJvdXJob29kSW5kZXgobWlsbykpKSAlPiUKICBsZWZ0X2pvaW4ocGl2b3RfbG9uZ2VyKGNlbGx0eXBlLmRmLCBjb2xzPS0gYyhuaEluZGV4LCBjZWxsLnR5cGVzKSwgbmFtZXNfdG8gPSAiZ2VuZSIsIHZhbHVlc190byA9ICJjb3VudHMiKSAsIGJ5PSJuaEluZGV4IikgJT4lCiAgIyBncm91cF9ieShnZW5lKSAlPiUKICAjIG11dGF0ZSh6c2NvcmUgPSBzY2FsZShsb2cxcChjb3VudHMpKSkgJT4lCiAgZmlsdGVyKGNlbGwudHlwZXM9PSJEUChQKSIpICU+JQogIGZpbHRlcihTcGF0aWFsRkRSIDwgMC4xKSAlPiUKICBtdXRhdGUoZ3JvdXA9aWZlbHNlKGxvZ0ZDID4gMCwgImxhdGUiLCAiZWFybHkiKSkgJT4lCiAgYXJyYW5nZShjb3VudHMpICU+JQogICMgZmlsdGVyKGdlbmU9PSJDRDhBIikgJT4lCiAgZ2dwbG90KGFlcyhncm91cCwgY291bnRzKSkgKwogIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIpICsKICBnZW9tX2ppdHRlcihzaXplPTAuMSwgd2lkdGg9MC4xKSArCiAgZmFjZXRfd3JhcChnZW5lfi4sIHNjYWxlcz0iZnJlZV95IikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCgogIApgYGAKYGBge3J9CmRmIDwtIHBsb3RNaWxvUmVkdWNlZERpbShtaWxvLCBtaWxvX3Jlc3VsdHMgPSBtaWxvX3Jlcywgc3BsaXRfYnkgPSAiY2VsbCB0eXBlcyIsIGZpbHRlcl9hbHBoYSA9IDAuMSkkZGF0YQpkZiAlPiUgCiAgZmlsdGVyKHNwbGl0X2J5PT0iRFAoUCkiKSAlPiUKICBnZ3Bsb3QoYWVzKFgsWSkpICsgZ2VvbV9wb2ludCgpIAoKZHBwX21pbG8gPC0gbWlsb1ssd2hpY2gobWlsbyRgY2VsbCB0eXBlc2A9PSJEUChQKSIpXQoKc2NhdGVyOjpwbG90VU1BUChkcHBfbWlsbywgY29sb3VyX2J5PWMoIkNEM0QiKSkKYGBgCgo8IS0tIENvbXBhcmUgbG9nLUZDIGFuZCBwcm9wb3J0aW9ucyAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIGNlbGx0eXBlLmRmIDwtIGRhdGEuZnJhbWUobmhJbmRleD0xOm5jb2wobWlsbyksIGNlbGwudHlwZXM9Y29sRGF0YShtaWxvKVssImNlbGwgdHlwZXMiXSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2U9Y29sRGF0YShtaWxvKVssIkFnZSJdKSAtLT4KPCEtLSBsZWZ0X2pvaW4oY2VsbHR5cGUuZGYsIG11dGF0ZShtaWxvX3JlcywgbmhJbmRleCA9IHVubGlzdChuZWlnaGJvdXJob29kSW5kZXgobWlsbykpKSwgYnk9Im5oSW5kZXgiKSAgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoQWdlKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUodG90X2FnZT1uKCkpICU+JSAtLT4KPCEtLSAgIHVuZ3JvdXAoKSAlPiUgLS0+CjwhLS0gICBncm91cF9ieShjZWxsLnR5cGVzLCBBZ2UpICU+JSAtLT4KPCEtLSAgIHN1bW1hcmlzZShtZWFuX2xvZ0ZDID0gbWVhbihsb2dGQywgbmEucm09VCksIHNkX2xvZ0ZDID0gc2QobG9nRkMsIG5hLnJtPVQpLCBwZXJjX2FnZT1uKCkvdG90X2FnZSkgJT4lIC0tPgo8IS0tICAgZ2dwbG90KGFlcyhBZ2UsIG1lYW5fbG9nRkMsIGNvbG9yPUFnZSkpICsgIC0tPgo8IS0tICAgZ2VvbV9wb2ludCgpICsgLS0+CjwhLS0gICAjIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bWVhbl9sb2dGQy1zZF9sb2dGQywgeW1heD1tZWFuX2xvZ0ZDICsgc2RfbG9nRkMpLCB3aWR0aD0wLjMpICsgLS0+CjwhLS0gICBmYWNldF93cmFwKGNlbGwudHlwZXN+Liwgc2NhbGVzPSJmcmVlX3kiKSAtLT4KCjwhLS0gbGVmdF9qb2luKGNlbGx0eXBlLmRmLCBtdXRhdGUobWlsb19yZXMsIG5oSW5kZXggPSB1bmxpc3QobmVpZ2hib3VyaG9vZEluZGV4KG1pbG8pKSksIGJ5PSJuaEluZGV4IikgICU+JSAtLT4KPCEtLSAgIGdyb3VwX2J5KEFnZSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKHRvdF9hZ2U9bigpKSAlPiUgLS0+CjwhLS0gICB1bmdyb3VwKCkgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoY2VsbC50eXBlcywgQWdlKSAlPiUgLS0+CjwhLS0gICBzdW1tYXJpc2UobWVhbl9sb2dGQyA9IG1lYW4obG9nRkMsIG5hLnJtPVQpLCBzZF9sb2dGQyA9IHNkKGxvZ0ZDLCBuYS5ybT1UKSwgcGVyY19hZ2U9bigpL3RvdF9hZ2UpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoQWdlLCBwZXJjX2FnZSwgY29sb3I9QWdlKSkgKyAgLS0+CjwhLS0gICBnZW9tX3BvaW50KCkgKyAtLT4KPCEtLSAgICMgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuX2xvZ0ZDLXNkX2xvZ0ZDLCB5bWF4PW1lYW5fbG9nRkMgKyBzZF9sb2dGQyksIHdpZHRoPTAuMykgKyAtLT4KPCEtLSAgIGZhY2V0X3dyYXAoY2VsbC50eXBlc34uLCBzY2FsZXM9ImZyZWVfeSIpIC0tPgo8IS0tIGBgYCAtLT4KCgojIyBDb21wYXJlIGVucmljaG1lbnQgYW5kIHBzZXVkb3RpbWUgb3JkZXJpbmcKYGBge3J9CgpgYGAKCgoKPCEtLSAjIyBGaWx0ZXJpbmcganVzdCBBZ2V4IGZvciB3aGljaCB5b3UgaGF2ZSA+IDIgc2FtcGxlcyAtLT4KCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSB0aC5tZXRhIDwtIGRhdGEuZnJhbWUoY29sRGF0YShzY2UpWyxjKCJTYW1wbGUiLCJBZ2UiKV0pICAtLT4KPCEtLSBrZWVwLmFnZXMgPC0gYygnMTF3JywnMTJ3JywnMTN3JywnMTR3JywnMTZ3JywnMTd3JykgLS0+Cgo8IS0tIHRoLm1ldGEkQWdlIDwtIG9yZGVyZWQodGgubWV0YSRBZ2UsIGxldmVscz1jKCc3dycsJzh3JywnOXcnLCcxMHcnLCcxMXcnLCcxMncnLCcxM3cnLCcxNHcnLCcxNncnLCcxN3cnKSkgLS0+CjwhLS0gdGgubWV0YSA8LSAtLT4KPCEtLSAgIGRpc3RpbmN0KHRoLm1ldGEpICU+JSAtLT4KPCEtLSAgIGZpbHRlcihBZ2UgJWluJSBrZWVwLmFnZXMpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShBZ2UgPSBvcmRlcmVkKEFnZSwgbGV2ZWxzPWtlZXAuYWdlcykpICU+JSAtLT4KPCEtLSAgIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JSAtLT4KPCEtLSAgIHNlbGVjdChTYW1wbGUsIEFnZSkgJT4lIC0tPgo8IS0tICAgY29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGUiKSAtLT4KCjwhLS0gdGgubWV0YSAlPiUgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKEFnZSkpICsgZ2VvbV9iYXIoKSAtLT4KCjwhLS0gdGgubW9kZWwgPC0gbW9kZWwubWF0cml4KH4gIEFnZSwgZGF0YT10aC5tZXRhKSAtLT4KPCEtLSB0aC5tb2RlbCAtLT4KCjwhLS0gYGBgIC0tPgoKCjwhLS0gYGBge3J9IC0tPgo8IS0tICMgbWlsb19maWx0IDwtIG1pbG9bLHdoaWNoKG1pbG8kQWdlICVpbiUga2VlcC5hZ2VzKV0gLS0+Cgo8IS0tIG1pbG8gPC0gY291bnRDZWxscyhtaWxvLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLmZyYW1lKGNvbERhdGEobWlsbylbLGMoIlNhbXBsZSIsIkFnZSIpXSksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICBzYW1wbGVzID0gIlNhbXBsZSIpIC0tPgoKCjwhLS0gbWlsb0BuZWlnaGJvdXJob29kQ291bnRzIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIGdyYXBoX3NwYXRpYWxGRFIgPC0gZnVuY3Rpb24obmVpZ2hib3Job29kcywgZ3JhcGgsIHB2YWx1ZXMsIGNvbm5lY3Rpdml0eT0ndmVydGV4JywgcGNhPU5VTEwpeyAtLT4KPCEtLSAgICMgaW5wdXQgYSBzZXQgb2YgbmVpZ2hib3Job29kcyBhcyBhIGxpc3Qgb2YgZ3JhcGggdmVydGljZXMgLS0+CjwhLS0gICAjIHRoZSBpbnB1dCBncmFwaCBhbmQgdGhlIHVuYWRqdXN0ZWQgR0xNIHAtdmFsdWVzIC0tPgo8IS0tICAgIycgbmVpZ2hib3Job29kczogbGlzdCBvZiB2ZXJ0aWNlcyBhbmQgdGhlaXIgcmVzcGVjdGl2ZSBuZWlnaGJvcmhvb2RzIC0tPgo8IS0tICAgIycgZ3JhcGg6IGlucHV0IGtOTiBncmFwaCAtLT4KPCEtLSAgICMnIHB2YWx1ZXM6IGEgdmVjdG9yIG9mIHB2YWx1ZXMgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlIG5laWdoYm9yaG9vZCBpbmRpY2VzIC0tPgo8IS0tICAgIycgY29ubmVjdGl2aXR5OiBjaGFyYWN0ZXIgLSBlZGdlIG9yIHZlcnRleCB0byBjYWxjdWxhdGUgbmVpZ2hib3Job29kIGNvbm5lY3Rpdml0eSBvciBkaXN0YW5jZSB0byB1c2UgYXZlcmFnZSBFdWNsaWRlYW4gZGlzdGFuY2UgLS0+CjwhLS0gICAjJyBwY2E6IG1hdHJpeCBvZiBQQ3MgdG8gY2FsY3VsYXRlIEV1Y2xpZGVhbiBkaXN0YW5jZXMsIG9ubHkgcmVxdWlyZWQgd2hlbiBjb25uZWN0aXZpdHkgPT0gZGlzdGFuY2UgLS0+CjwhLS0gICAjIERpc2NhcmRpbmcgTkEgcHZhbHVlcy4gLS0+CjwhLS0gICBoYXNwdmFsIDwtICFpcy5uYShwdmFsdWVzKSAtLT4KPCEtLSAgIGlmICghYWxsKGhhc3B2YWwpKSB7IC0tPgo8IS0tICAgICAgIGNvb3JkcyA8LSBjb29yZHNbaGFzcHZhbCwgLCBkcm9wPUZBTFNFXSAtLT4KPCEtLSAgICAgICBwdmFsdWVzIDwtIHB2YWx1ZXNbaGFzcHZhbF0gLS0+CjwhLS0gICB9IC0tPgoKPCEtLSAgICMgZGVmaW5lIHRoZSBzdWJncmFwaCBmb3IgZWFjaCBuZWlnaGJvcmhvb2QgdGhlbiBjYWxjdWxhdGUgdGhlIHZlcnRleCBjb25uZWN0aXZpdHkgZm9yIGVhY2ggLS0+CjwhLS0gICAjIHRoaXMgbGF0dGVyIGNvbXB1dGF0aW9uIGlzIHF1aXRlIHNsb3cgLSBjYW4gaXQgYmUgc3BlZCB1cD8gLS0+CjwhLS0gICBzdWJncmFwaHMgPC0gbGFwcGx5KDE6bGVuZ3RoKG5laWdoYm9yaG9vZHNbaGFzcHZhbF0pLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOPWZ1bmN0aW9uKFgpIGluZHVjZWRfc3ViZ3JhcGgoZ3JhcGgsIG5laWdoYm9yaG9vZHNbaGFzcHZhbF1bW1hdXSkpIC0tPgo8IS0tICAgIyBub3cgbG9vcCBvdmVyIHRoZXNlIHN1Yi1ncmFwaHMgdG8gY2FsY3VsYXRlIHRoZSBjb25uZWN0aXZpdHkgLSB0aGlzIHNlZW1zIGEgbGl0dGxlIHNsb3cuLi4gLS0+CjwhLS0gICBpZihjb25uZWN0aXZpdHkgPT0gInZlcnRleCIpeyAtLT4KPCEtLSAgICAgdC5jb25uZWN0IDwtIGxhcHBseShzdWJncmFwaHMsIEZVTj1mdW5jdGlvbihFRykgdmVydGV4X2Nvbm5lY3Rpdml0eShFRykpIC0tPgo8IS0tICAgfSBlbHNlIGlmKGNvbm5lY3Rpdml0eSA9PSAiZWRnZSIpeyAtLT4KPCEtLSAgICAgdC5jb25uZWN0IDwtIGxhcHBseShzdWJncmFwaHMsIEZVTj1mdW5jdGlvbihFRykgZWRnZV9jb25uZWN0aXZpdHkoRUcpKSAtLT4KPCEtLSAgIH0gZWxzZSBpZihjb25uZWN0aXZpdHkgPT0gImRpc3RhbmNlIil7IC0tPgo8IS0tICAgICBpZighaXMubnVsbChwY2EpKXsgLS0+CjwhLS0gICAgICAgdC5jb25uZWN0IDwtIGxhcHBseSgxOmxlbmd0aChuZWlnaGJvcmhvb2RzW2hhc3B2YWxdKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgRlVOPWZ1bmN0aW9uKFBHKSB7IC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5wY3MgPC0gcGNhW25laWdoYm9yaG9vZHNbaGFzcHZhbF1bW1BHXV0sIF0gLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICB4LmV1Y2xpZCA8LSBhcy5tYXRyaXgoZGlzdCh4LnBjcykpIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgeC5kaXN0ZGVucyA8LSAxL21lYW4oeC5ldWNsaWRbbG93ZXIudHJpKHguZXVjbGlkLCBkaWFnPUZBTFNFKV0pIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybih4LmRpc3RkZW5zKX0pIC0tPgo8IS0tICAgICB9IGVsc2V7IC0tPgo8IS0tICAgICAgIHN0b3AoIkEgbWF0cml4IG9mIFBDcyBpcyByZXF1aXJlZCB0byBjYWxjdWxhdGUgZGlzdGFuY2VzIikgICAtLT4KPCEtLSAgICAgfSAtLT4KPCEtLSAgIH1lbHNleyAtLT4KPCEtLSAgICAgc3RvcCgiY29ubmVjdGl2aXR5IG9wdGlvbiBub3QgcmVjb2duaXNlZCAtIG11c3QgYmUgZWl0aGVyIGVkZ2UsIHZlcnRleCBvciBkaXN0YW5jZSIpIC0tPgo8IS0tICAgfSAtLT4KCjwhLS0gICAjIHVzZSAxL2Nvbm5lY3Rpdml0eSBhcyB0aGUgd2VpZ2h0aW5nIGZvciB0aGUgd2VpZ2h0ZWQgQkggYWRqdXN0bWVudCBmcm9tIEN5ZGFyIC0tPgo8IS0tICAgdyA8LSAxL3VubGlzdCh0LmNvbm5lY3QpIC0tPgo8IS0tICAgd1tpcy5pbmZpbml0ZSh3KV0gPC0gMCAtLT4KCjwhLS0gICAjIENvbXB1dGluZyBhIGRlbnNpdHktd2VpZ2h0ZWQgcS12YWx1ZS4gLS0+CjwhLS0gICBvIDwtIG9yZGVyKHB2YWx1ZXMpIC0tPgo8IS0tICAgcHZhbHVlcyA8LSBwdmFsdWVzW29dIC0tPgo8IS0tICAgdyA8LSB3W29dIC0tPgo8IS0tICAgYWRqcCA8LSBudW1lcmljKGxlbmd0aChvKSkgLS0+CjwhLS0gICBhZGpwW29dIDwtIHJldihjdW1taW4ocmV2KHN1bSh3KSpwdmFsdWVzL2N1bXN1bSh3KSkpKSAtLT4KPCEtLSAgIGFkanAgPC0gcG1pbihhZGpwLCAxKSAtLT4KPCEtLSAgIGlmICghYWxsKGhhc3B2YWwpKSB7IC0tPgo8IS0tICAgICByZWZwIDwtIHJlcChOQV9yZWFsXywgbGVuZ3RoKGhhc3B2YWwpKSAtLT4KPCEtLSAgICAgcmVmcFtoYXNwdmFsXSA8LSBhZGpwIC0tPgo8IS0tICAgICBhZGpwIDwtIHJlZnAgLS0+CjwhLS0gICAgIH0gLS0+CjwhLS0gICByZXR1cm4oYWRqcCkgLS0+CjwhLS0gfSAtLT4KCjwhLS0gIyB0ZXN0UUxGIDwtIGZ1bmN0aW9uKGdyYXBoLCBuaF9jb3VudHMsIHRoLm1vZGVsLCBjb25uZWN0aXZpdHk9J2VkZ2UnLCBwY2E9TlVMTCl7IC0tPgo8IS0tIG5oX2NvdW50cyA8LSBtaWxvQG5laWdoYm91cmhvb2RDb3VudHMgLS0+Cgo8IS0tIGRnZSA8LSBER0VMaXN0KG5oX2NvdW50c1ssIHJvd25hbWVzKHRoLm1vZGVsKV0sIGxpYi5zaXplPWxvZyhjb2xTdW1zKG5oX2NvdW50cykpKSAtLT4KPCEtLSBkZ2UgPC0gZXN0aW1hdGVEaXNwKGRnZSwgdGgubW9kZWwpIC0tPgoKPCEtLSBmaXQgPC0gZ2xtUUxGaXQoZGdlLCB0aC5tb2RlbCwgcm9idXN0PVRSVUUpIC0tPgo8IS0tICMgc2ltMi5jb250cmFzdCA8LSBtYWtlQ29udHJhc3RzKENvbmRpdGlvbkEgLSBDb25kaXRpb25CLCBsZXZlbHM9dGgubW9kZWwpIC0tPgo8IS0tICMgICBzaW0yLnJlcyA8LSBnbG1RTEZUZXN0KHNpbTIuZml0LCBjb250cmFzdD1zaW0yLmNvbnRyYXN0KSAtLT4KPCEtLSBtaWxvX3JlcyA8LSBhcy5kYXRhLmZyYW1lKHRvcFRhZ3MoZ2xtUUxGVGVzdChmaXQsIGNvZWY9MSksIHNvcnQuYnk9J25vbmUnLCBuPUluZikpIC0tPgo8IS0tIG1pbG9fcmVzJFNpZyA8LSBhcy5mYWN0b3IoYXMubnVtZXJpYyhtaWxvX3JlcyRGRFIgPD0gMC4wNSkpIC0tPgo8IS0tIG1pbG9fcmVzJE5laWdoYm91cmhvb2QgPC0gYXMubnVtZXJpYyhyb3duYW1lcyhtaWxvX3JlcykpIC0tPgoKPCEtLSBzaW0yLnNwYXRpYWxmZHIgPC0gZ3JhcGhfc3BhdGlhbEZEUihuZWlnaGJvcmhvb2RzPW1pbG9AbmVpZ2hib3VyaG9vZHMsICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaD1taWxvQGdyYXBoW1siZ3JhcGgiXV0sIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbm5lY3Rpdml0eT0iZGlzdGFuY2UiLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlcz1taWxvX3JlcyRQVmFsdWUsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBjYT1yZWR1Y2VkRGltKG1pbG8sIk1PRkEiKSAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIC0tPgoKPCEtLSBgYGAgLS0+CgoKPCEtLSAjIyBQaWNraW5nIGsgcGFyYW1ldGVyIC0tPgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gdGVzdF9tZWFuX25oX3NpemUgPC0gZnVuY3Rpb24obSwgcHJvcCwgaywgbl9jZWxscywgZD0zMCl7IC0tPgo8IS0tICAgbSA8LSBtWyxzYW1wbGUoY29sbmFtZXMobSksIHNpemUgPSBuX2NlbGxzKV0gLS0+CjwhLS0gICBtIDwtIGJ1aWxkR3JhcGgobSwgayA9IGssIGQgPSBkKSAtLT4KPCEtLSAgIHJlZmluZWRfbmggPC0gbmVpZ2hib3VyaG9vZHMobWFrZU5laWdoYm91cmhvb2RzKG0sIHByb3A9cHJvcCwgaz1rLCBkPWQsIHJlZmluZWQgPSBUUlVFLCBzZWVkPTQyKSkgLS0+CjwhLS0gICByZXR1cm4obWVhbihzYXBwbHkocmVmaW5lZF9uaCwgbGVuZ3RoKSkpIC0tPgo8IS0tIH0gLS0+Cgo8IS0tIGtfdmVjIDwtIHNlcSgxMCw1MCwgYnk9NSkgLS0+CjwhLS0gIyBuY2VsbHNfdmVjIDwtIHJvdW5kKG5jb2wobWlsbykqc2VxKDAuMSwxLCBieT0wLjEpLDApIC0tPgoKPCEtLSBncmlkX2RmIDwtIGV4cGFuZC5ncmlkKG5jZWxsc192ZWMsIGtfdmVjKSAtLT4KPCEtLSBjb2xuYW1lcyhncmlkX2RmKSA8LSBjKCJuX2NlbGxzIiwgImsiKSAtLT4KPCEtLSBtZWFuX25oX3NpemVzX3RoIDwtIGFwcGx5KGdyaWRfZGYsIDEsIGZ1bmN0aW9uKHgpIHRlc3RfbWVhbl9uaF9zaXplKG1pbG8sIHByb3AgPSAwLjIsIHhbImsiXSwgeFsibl9jZWxscyJdLCBkPTUpKSAtLT4KCjwhLS0gdGVzdF9tZWFuX25oX3NpemUobWlsbywgcHJvcCA9IDAuMiwgZ3JpZF9kZlsxLCJrIl0sIGdyaWRfZGZbMSwibl9jZWxscyJdLCBkPTUpIC0tPgo8IS0tIGBgYCAtLT4KCgoK